# Menu - Menu has a list of menuitems and it allows user to select one of them.
# Copyright (C) 2007 Lauri Taimila
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

__licence__ = "GPLv2"
__copyright__ = "2007, Lauri Taimila"
__author__ = "Lauri Taimila <lauri@taimila.com>"

import clutter
from frontend.gui.widgets.arrow_texture import ArrowTexture

class Menu(clutter.Group):
    """
    Menu widget.
    
    Blaa blaa...
    """

    def __init__(self):
        """
        Initialize menu widget.
        """
        clutter.Group.__init__(self)
        self.__name = None        # Menu name
        self.__active = False     # Is this menu active
        self.__page_zize = 8      # How many items are displayed at the same time
        self.__loop = False       # Should menu loop from last to first
        self.__selector = None    # Selector widget
        self.__items = []         # List of MenuItems
        self.__current = 0        # Current position of the menu
        self.__item_width = 0     # Menuitem width
        self.__item_height = 0    # Menuitem height
        
    def is_active(self):
        """
        Is this menu active.
        @return boolean, True if menu is currently active, otherwise False
        """
        return self.__active
    
    def set_active(self, boolean):
        """
        Set this widget active or inactive
        @param boolean: True to set widget active, False to set inactive
        """
        self.__active = boolean
        if boolean:
            if self.__selector is not None:
                self.__selector.show()
            self.get_current_menuitem().set_active(True)
            self.set_opacity(255)
        else:
            if self.__selector is not None:
                self.__selector.hide()
            self.get_current_menuitem().set_active(False)
            self.set_opacity(128)
        
    def add(self, menuitem):
        """
        Add new menuitem to the menu.
        @param menuitem: Add this menuitem (Menitem Widget)
        """
        # First menuitem determines the size of all menuitems (is it good?)
        if len(self.__items) == 0:
            self.__item_width = menuitem.get_width()
            self.__item_height = menuitem.get_height()
            
        menuitem.show()
        menuitem.set_position(0, len(self.__items) * menuitem.get_height())
        self.__items.append(menuitem)
        clutter.Group.add(self, menuitem)
        self.update_clip()
        
    def remove(self, menuitem):
        """
        Remove menuitem from the menu.
        @param menuitem: Remove this menuitem (Menitem Widget)
        """
        clutter.Group.remove(self, menuitem)
        for item in self.__items:
            if item is menuitem:
                del item
                
    def remove_all(self):
        """
        Remove all menuitems from the menu.
        @param menuitem: Remove this menuitem (Clutter.Actor)
        """
        clutter.Group.remove_all(self)
        self.__current = 0
        del self.__selector
        self.__selector = None
        del self.__items
        self.__items = []
    
    def set_loop(self, boolean):
        """
        Set loop for menu. If loop is true then first menuitem comes after the
        last one.
        @param boolean: True if menu should loop, otherwise False
        """
        self.__loop = boolean
        
    def is_loop(self):
        """
        Is menu in loop mode.
        @return: Boolean value
        """
        return self.__loop
    
    def set_menuitem_background(self, texture):
        """
        Set texture that is displayed under every menuitem. This is optional.
        @param texture: Menuitem background texture. (clutter.Actor)
        """
        for index in range(len(self.__items)):
            bg = clutter.CloneTexture(texture)
            bg.set_size(self.get_width(), self.__item_height)
            bg.set_position(0, index * self.__item_height)
            bg.set_opacity(128)
            bg.set_name("background")
            bg.show()
            clutter.Group.add(self, bg)
            for item in self.__items:
                clutter.Group.lower(self, bg, item)
        
    def set_selector(self, selector):
        """
        Set selector texture. Selector is moved over menuitems when user
        browses the menu.
        @param selector: Selector texture (Clutter.Actor)
        """
        selector.set_position(0,0)
        selector.set_size(self.get_width(), self.__item_height)
        height = selector.get_height()
        x_scale = self.get_width() / float(selector.get_width())
        y_scale = self.__item_height / float(selector.get_height())
        selector.set_scale(x_scale, y_scale)
        selector.set_name("selector")
        self.__selector = selector
        clutter.Group.add(self, self.__selector)
        for item in self.__items:
            if item.get_name() != "background":
                clutter.Group.lower(self, selector, item)
        
    def scroll_up(self):
        """
        Scroll menu up (one menuitem).
        """
        if self.__current > 0:
            self.__items[self.__current].set_active(False)
            self.__current = self.__current - 1
            self.__items[self.__current].set_active(True)
            if self.__selector is not None:
                self.__selector.set_position(0, self.__current * self.__item_height)
        
        # Loop from top to bottom
        elif self.__current == 0 and self.__loop:
            self.__items[self.__current].set_active(False)
            self.__current = len(self.__items) - 1
            self.__items[self.__current].set_active(True)
            if self.__selector is not None:
                self.__selector.set_position(0, self.__current * self.__item_height)
        
    def scroll_down(self):
        """
        Scroll menu down (one menuitem).
        """
        if self.__current < len(self.__items) - 1:
            self.__items[self.__current].set_active(False)
            self.__current = self.__current + 1
            self.__items[self.__current].set_active(True)
            if self.__selector is not None:
                self.__selector.set_position(0, self.__current * self.__item_height)
        
        # Loop from bottom to top
        elif self.__current == len(self.__items) - 1 and self.__loop:
            self.__items[self.__current].set_active(False)
            self.__current = 0
            self.__items[self.__current].set_active(True)
            if self.__selector is not None:
                self.__selector.set_position(0, self.__current * self.__item_height)
        
    def scroll_up_page(self):
        """
        Scroll menu up one page. One page is a number of visible items.
        """
        
    def scroll_down_page(self):
        """
        Scroll menu down one page. One page is a number of visible items.
        """
        # Set current menuitem
        self.__items[self.__current].set_active(False)
        self.__current = self.__current + 8 #FIXME: What if there is not that many?
        self.__items[self.__current].set_active(True)
        
        # Set selector position
        if self.__selector is not None:
                self.__selector.set_position(0, self.__current * self.__items[0].get_height())
        
        # Clip to current position of the menu
        #self.set_clip(0,)
    
    def update_clip(self):
        """
        Update menu clipping. This method decides which partion of menu is
        actually displayed on screen. If you have a long menu, not all items
        fit into the window. This function checks what is the current position
        of the menulist and then clips the widget in such a way that current
        position is on screen.
        """
        if len(self.__items) <= self.__page_zize:
            self.remove_clip()
        else:
            self.set_clip(0,
                          self.__current * self.__item_height,
                          self.__item_width, 
                          self.__item_height * self.__page_zize)
            self.set_height(self.__item_height * self.__page_zize)
        
    def get_current_postition(self):
        """
        Get current position of the list. This position is under selector.
        """
        return self.__current
        
    def get_current_menuitem(self):
        """
        Get Clutter.Actor object from the current postition.
        """
        if len(self.__items) == 0:
            return None
        return self.__items[self.__current]
    
    def get_number_of_items(self):
        """
        Get number of menuitems in menu.
        """
        return len(self.__items)
    
    def set_name(self, name):
        """
        Set menu name
        @param name: Name of the menu (used to identify menu from others)
        """
        self.__name = name
        
    def get_name(self):
        """
        Get name of the menu.
        @return name as String
        """
        return self.__name